//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	SecurityConceptMatrixBased.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.ComponentModel;

namespace Smart.Common.Security
{
	/// <summary>
	/// Security matrix based security concept
	/// </summary>
	[Localizable(false)]
	public abstract class SecurityConceptMatrixBased : SecurityConcept
	{
		/// <summary>
		/// Init cache control
		/// </summary>
		static SecurityConceptMatrixBased()
		{
			SetCacheControl();
		}

		/// <summary>
		/// Set cache control handlers
		/// </summary>
		static void SetCacheControl()
		{
			SPServices.Cache.CacheClearing += CacheClearing;
		}

		/// <summary>
		/// Cache clearing handler
		/// </summary>
		static void CacheClearing()
		{
			Cache.Clear();
		}

		/// <summary>
		/// Security matrix based security concept
		/// </summary>
		protected SecurityConceptMatrixBased(SecurityContext ctx)
			: base(ctx)
		{
		}

		/// <summary>
		/// Matrix cache
		/// </summary>
		private static readonly SyncDictionary<string, SecurityMatrix> Cache = new SyncDictionary<string, SecurityMatrix>(StringComparer.OrdinalIgnoreCase);

		/// <summary>
		/// Returns security matrix cache key
		/// </summary>
		public virtual string GetCacheKey(string matrixName, Guid siteId)
		{
			return string.Concat(siteId.GetHashCodeX(), matrixName);
		}

		/// <summary>
		/// Loads security matrix by name
		/// </summary>
		public virtual SecurityMatrix LoadMatrix(string matrixName)
		{
			if (matrixName.IsNullOrEmpty()) throw new ArgumentNullException("matrixName");

			var cacheKey = GetCacheKey(matrixName, Ctx.SiteID);

			var matrix = Cache.SafeGet(cacheKey);

			if (matrix != null)
				return matrix;

			var site = SPH.GetSite();
			var web = SPH.GetRootWeb();

			if (site.ID != Ctx.SiteID)
				throw new InvalidOperationException("SecurityConceptMatrixBased is used with different Site!");

			// Load only for root web settings
			var matrixXml =
				SPServices.Configuration.GetValue(
					site,
					web,
					null,
					null,
					string.Format(Const.Security.SecurityConfKeyTempl, matrixName),
					null);

			matrix = new SecurityMatrix(matrixName);

			if (!matrixXml.IsNullOrEmpty())
				matrix.ParseXml(matrixXml);

			Cache[cacheKey] = matrix;

			return matrix;
		}

		/// <summary>
		/// Return security matrix row
		/// </summary>
		/// <param name="matrixName">matrix name (policy name)</param>
		/// <param name="matrixRowName">matrix row name (policy param)</param>
		public virtual SecurityMatrixRow GetMatrixRow(string matrixName, string matrixRowName)
		{
			var m = LoadMatrix(matrixName);

			return m.Rows.SafeGet(matrixRowName);
		}

		/// <summary>
		/// Returns security assignments
		/// </summary>
		public override SecuritySecurableAssignments GetSecurityAssignments(SecurableType securableType, ISecurable securable, SecurityPolicy policy)
		{
			if (securableType == null) throw new ArgumentNullException("securableType");
			if (securable == null) throw new ArgumentNullException("securable");
			if (policy == null) throw new ArgumentNullException("policy");

			var row = GetMatrixRow(policy.Name, policy.Value);

			if (row == null)
				return null;

			var assignments = CreateAssignments(securableType, securable, policy, row.Inherit, row.CurrentScopeOnly);

			foreach (var securityAssignment in row.Assignments.Values)
				assignments.Add(GetSecurityPrincipal(securityAssignment.Principal), securityAssignment.AccessLevel);

			return assignments;
		}
	}
}